package cipher

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"log"
)

/**
私钥封装接口
只用来解密和签名
 */
type PrivateCipher interface {
	//PrivateCipher这个接口不需要调用SetPublicKey，一个对象一般在系统中只有一个，New的时候直接就设置好了
	SetPrivateKey(privateKey []byte)(err error)
	//解密数据
	Decrypt(src []byte) (dst []byte, err error)
	//对数据进行签名
	Sign(str []byte)(dst []byte, err error)
}


/**
公钥封装接口
只用来加密和验签
 */
type PublicCipher interface {
	//针对没同的用户可能要改SetPublicKey，针对不同的用户可以New不同的对象
	SetPublicKey(publicKey []byte)(err error)
	//加密数据
	Encrypt(src []byte) (dst []byte, err error)
	//验证签名
	VerifySign(src,sig []byte)(isOk bool, err error)
}


//生成一个Ecc私钥对象，
// 解密
// 签名
//privateKey形如以下形式
/**
-----BEGIN ECDSA PRIVATE KEY-----
MHcCAQEEIAkTxqgM3W7WpOQVcAPSMaI7Sykq0AhDoyc/p/pqw7ZsoAoGCCqGSM49
AwEHoUQDQgAEZ4gpl3nkAeCcE2FaJ4NC3cWw6/PlVbzDzMTzA8NGRisfgOfb2AAf
9d40Ohc2P3cOGBWG2q9mU/8q2SqUEvte3g==
-----END ECDSA PRIVATE KEY-----
 */
func NewEccPrivate(privateKey []byte) PrivateCipher  {

	eccPrivateCipher := &EccPrivateCipher{}
	err:=eccPrivateCipher.SetPrivateKey(privateKey)
	if err != nil {
		panic(err.Error())
	}
	return eccPrivateCipher


}

/**
生成一个Ecc公钥对象
加密
验签
publicKey形如以下形式
-----BEGIN ECDSA PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZ4gpl3nkAeCcE2FaJ4NC3cWw6/Pl
VbzDzMTzA8NGRisfgOfb2AAf9d40Ohc2P3cOGBWG2q9mU/8q2SqUEvte3g==
-----END ECDSA PUBLIC KEY-----
 */
func NewEccPublic(publicKey []byte) PublicCipher  {

	eccPublicCipher := &EccPublicCipher{}
	err:=eccPublicCipher.SetPublicKey(publicKey)
	if err != nil {
		panic(err.Error())
	}
	return eccPublicCipher


}

//生成一个Rsa私钥对象，
// 解密
// 签名
//privateKey形如以下形式
/**
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCwt4ebC+56oaZL0iOREr/Eb6TqPJB5+V/xHekfxDNN44ROZHZI
ByL7C9B76MIANRDBDj11UA1KPOiUXcW4SURLjiMt8t4kpjJEVM2DBW0ql96DicEz
mvAdH0GLY8LMjPOC4LVVDt5CH5BF5xsTSGx2BW53dsPTkKBrnDrXRdcxywIDAQAB
AoGASE7QbeCz8/mw0dVqODIzxWzpTe76Lu6bE03p1dWyKGyv9mOlAlPpPE/ZPto+
h1cvQ89P1nCq/3VstUipXn6cMp/ZWWyCM8a4+Ap+OaTeJ7mbW608hPYYg/nmahhr
caGiYq0i4C2ZCFyrE+Sx/rtRS6DS1MV80YYEYO1DinZdKgECQQDhlarn+T5h8qaQ
EQm193Hz2MfDK83A16vpOeOevW+2tiVo6YZYeHNKIVLR0p/ZRFI+TwxhSiEqZhdC
peC5WViFAkEAyIsh107mytj7034dVB7XkuK0XKc/fPIB1lAEpkQyjfQBrEmKvJKB
VnhC+ILEQwNqLEEfFyPwsStLUoNS3V+aDwJBAMb5SqweDkY6z2vXe2dDgQGCvoCK
DIwCL6Ih64HCudzBQLIO+O7kH1zNk817KH9tYdWPx+2wgxxwvCePRG54PjkCQDFm
lJ+FQDUX3nDq0C64qkH0tplcWbddpjY03/BVPF7bjM30D8kEDfhPB7BQBii73puo
LxB0OgZqs9NPpkoCEuECQQDLK+bKRu4sDNPEet89y7IzMtOf5x8ryhiqm6DDeQTC
ar/Tri1bFC4/z8/2/PUcwtN3+rVUpRjKzD2QTPq8gRcx
-----END RSA PRIVATE KEY-----
 */
func NewRsaPrivate(privateKey []byte) PrivateCipher  {

	rsaPrivateCipher := &RsaPrivateCipher{}
	err:=rsaPrivateCipher.SetPrivateKey(privateKey)
	if err != nil {
		panic(err.Error())
	}
	return rsaPrivateCipher


}

/**
生成一个Ecc公钥对象
加密
验签
publicKey形如以下形式
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwt4ebC+56oaZL0iOREr/Eb6Tq
PJB5+V/xHekfxDNN44ROZHZIByL7C9B76MIANRDBDj11UA1KPOiUXcW4SURLjiMt
8t4kpjJEVM2DBW0ql96DicEzmvAdH0GLY8LMjPOC4LVVDt5CH5BF5xsTSGx2BW53
dsPTkKBrnDrXRdcxywIDAQAB
-----END PUBLIC KEY-----
 */
func NewRsaPublic(publicKey []byte) PublicCipher  {

	rsaPublicCipher := &RsaPublicCipher{}
	err:=rsaPublicCipher.SetPublicKey(publicKey)
	if err != nil {
		panic(err.Error())
	}
	return rsaPublicCipher


}



// 生成Ecc曲线的秘钥对
// 用elliptic.P256()就行了，其他的加解密不支持
//要保存文件的话，如下
//pri,pub,err:=GenEccKeyPair(elliptic.P256())
//err=ioutil.WriteFile("ecc_pri.pem",pri,os.FileMode(0644))
//err=ioutil.WriteFile("ecc_pub.pem",pub,os.FileMode(0644))
func GenEccKeyPair(curve elliptic.Curve) (priKey,pubKey []byte,err error) {

	var (
		privateKey     *ecdsa.PrivateKey
		privDerText    []byte
		privBlock      pem.Block
		//privFileHandle *os.File
		pubDerText     []byte
		pubBlock       pem.Block
		//pubFileHandle  *os.File
	)

	// 1. 使用指定的曲线生成私钥
	if privateKey, err = ecdsa.GenerateKey(curve, rand.Reader); err != nil {
		return
	}

	// 2. 使用x509编码
	if privDerText, err = x509.MarshalECPrivateKey(privateKey); err != nil {
		return
	}
	// 3. 生成私钥的block
	privBlock = pem.Block{
		Type:    "ECDSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privDerText,
	}
	// 4. 创建私钥存储的文件,并写入文件
	//if privFileHandle, err = os.Create(path.Join(fileDirectory, fmt.Sprintf("ecdsa_private_key_%s.pem", curve.Params().Name))); err != nil {
	//	return
	//}
	//4写入Buffer
	priBuf   := new(bytes.Buffer)
	// 5. 写入
	if err = pem.Encode(priBuf, &privBlock); err != nil {
		return
	}

	// 6. 使用x509编码
	if pubDerText, err = x509.MarshalPKIXPublicKey(&privateKey.PublicKey); err != nil {
		return
	}

	// 7. 生成公钥的block
	pubBlock = pem.Block{
		Type:    "ECDSA PUBLIC KEY",
		Headers: nil,
		Bytes:   pubDerText,
	}
	// 8. 创建公钥存储的文件,并写入文件
	//if pubFileHandle, err = os.Create(path.Join(fileDirectory, fmt.Sprintf("ecdsa_public_key_%s.pem", curve.Params().Name))); err != nil {
	//	return
	//}
	//4写入Buffer
	pubBuffer   := new(bytes.Buffer)

	// 9.写入
	if err = pem.Encode(pubBuffer, &pubBlock); err != nil {
		return
	}

	priKey = priBuf.Bytes()
	pubKey = pubBuffer.Bytes()
	return
}




//RSA公钥私钥产生
//要保存文件的话，如下
//pri,pub,err:=GenRsaKeyPair(1024)//2048
//err=ioutil.WriteFile("rsa_pri.pem",pri,os.FileMode(0644))
//err=ioutil.WriteFile("rsa_pub.pem",pub,os.FileMode(0644))
func GenRsaKeyPair(bits int)(priKey,pubKey []byte,err error){
	// 生成私钥文件

	//1.生成私钥
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return
	}
	//2.对生成的私钥进行编码处理，X509,按照规则，进行序列化处理，生成der编码的数据
	derStream := x509.MarshalPKCS1PrivateKey(privateKey)
	block := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: derStream,
	}
	priBuf   := new(bytes.Buffer)
	pubBuf   := new(bytes.Buffer)
	err = pem.Encode(priBuf, block)
	if err != nil {
		return
	}
	// 生成公钥文件
	publicKey := &privateKey.PublicKey
	derPkix, err := x509.MarshalPKIXPublicKey(publicKey)
	if err != nil {
		return
	}
	block = &pem.Block{
		Type:  "RSA PUBLIC KEY",
		Bytes: derPkix,
	}
	err = pem.Encode(pubBuf, block)
	if err != nil {
		return
	}
	priKey=priBuf.Bytes()
	pubKey=pubBuf.Bytes()
	return
}

/**
对文件签名
 */
func SignFile(cipher PrivateCipher,hash func(string) []byte,filepath string) []byte {

	if cipher==nil{
		log.Print("SignFile：私钥接口为空")
		return nil
	}
	if hash==nil{
		hash = SHA256File
		}
	sum:=hash(filepath)
	if sum==nil{
		log.Print("SignFile：计算文件hash为空")
		return nil
	}

	fsum,_:=cipher.Sign(sum)
	return fsum

}

/**
对文件签名进行验签
 */
func VerifySignFile(cipher PublicCipher,hash func(string) []byte,filepath string,fileSign []byte) bool {

	if cipher==nil{
		log.Print("VerifySignFile：私钥接口为空")
		return false
	}
	if hash==nil{
		hash = SHA256File
	}
	sum:=hash(filepath)
	if sum==nil{
		log.Print("VerifySignFile：计算文件hash为空")
		return false
	}

	ok,_:=cipher.VerifySign(sum,fileSign)
	return ok

}


